using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using net.Windward.WorkflowActions;
using System.Collections.ObjectModel;
using System.Security;
using DevExpress.Web.ASPxMenu;
using Microsoft.SharePoint;
using ArrowWebParts;

namespace net.Windward.NWAdapters
{
    public partial class RunReportNWActionDialog : System.Web.UI.Page
    {
        private enum MenuType
        {
            /// <summary>
            /// Menus for selecting a document library.
            /// </summary>
            DocLibSelector,
            /// <summary>
            /// Menus for selecting a document.
            /// </summary>
            DocSelector
        }

        // from http://www.dotnetspider.com/resources/1521-How-call-Postback-from-Javascript.aspx
        protected string UpdateRptrParms = null;
        private const string UPDATE_PARMS = "UPDATE_PARMS";
        private const string REQUEST_EVENT_ARG = "__EVENTARGUMENT";

        private static int parmInd = 0;

        protected void Page_Load(object sender, EventArgs e)
        {
            resetTopMessage();

            generateIsPostBackJSVar();

            if (!IsPostBack)
            {
                Menu mnuReportDefinition = (Menu)FindControlRecursive(Page, "mnuReportDefinition");
                populateMenu(mnuReportDefinition, MenuType.DocSelector);

                Menu mnuSaveInDocLib = (Menu)FindControlRecursive(Page, "mnuSaveInDocLib");
                populateMenu(mnuSaveInDocLib, MenuType.DocLibSelector);
            }

            // generate javascript postback method to update parameter repeater
            UpdateRptrParms = Page.ClientScript.GetPostBackEventReference(this, UPDATE_PARMS);

            bindRepeater();
        }

        /// <summary>
        /// Populates the specified menu so that it becomes a document library selector across all sites in
        /// the site collection.
        /// </summary>
        /// <param name="menu"></param>
        private void populateMenu(Menu menu, MenuType type)
        {
            try
            {
                System.Web.UI.WebControls.MenuItem root = new System.Web.UI.WebControls.MenuItem();
                root.Text = "Sites";
                root.Value = "Sites";
                menu.Items.Add(root);
                using (SPSite siteCollection = SPContext.Current.Site)
                {
                    foreach (SPWeb site in ArrowUtils.GetSPWebCollectionFromSite(siteCollection)) //modified by enjayh from 'foreach (SPWeb site in siteCollection.AllWebs)'
                        root.ChildItems.Add(createSiteMenuItem(site, type));
                }
            }
            catch (Exception ex)
            {
                displayError(ex.Message);
                return;
            }
        }

        /// <summary>
        /// Creates a MenuItem for the specified site (SPWeb). The returned MenuItem is a submenu, 
        /// listing each of the site's document libraries as simple menu items.
        /// </summary>
        /// <param name="site"></param>
        /// <returns>a MenuItem</returns>
        private System.Web.UI.WebControls.MenuItem createSiteMenuItem(SPWeb site, MenuType menuType)
        {
            System.Web.UI.WebControls.MenuItem siteMenuItem = new System.Web.UI.WebControls.MenuItem();
            siteMenuItem.Text = site.Title.ToString() + " Doc Libs";
            siteMenuItem.Value = site.Title.ToString() + " Doc Libs";
            foreach (SPList list in site.Lists)
            {
                if (list is SPDocumentLibrary && !list.Hidden)
                    siteMenuItem.ChildItems.Add(createSPListMenuItem(list, menuType));
            }
            return siteMenuItem;
        }

        /// <summary>
        /// Creates a menu item for the specified SPList.  If the menu is for selecting document libraries,
        /// then only a simple menu item will be returned.  If the menu item is for selecting documents,
        /// then the menu item will contain a sub-menu item for each document.
        /// </summary>
        /// <param name="list">the SPList to create this menu item for</param>
        /// <returns>a MenuItem</returns>
        private System.Web.UI.WebControls.MenuItem createSPListMenuItem(SPList list, MenuType menuType)
        {
            System.Web.UI.WebControls.MenuItem docLibItem = new System.Web.UI.WebControls.MenuItem();
            docLibItem.Text = list.Title;
            docLibItem.Value = 
                SPContext.Current.Site.MakeFullUrl(Uri.EscapeUriString(list.ParentWebUrl)) + " " + 
                SPContext.Current.Site.MakeFullUrl(Uri.EscapeUriString(list.DefaultViewUrl));

            if (menuType == MenuType.DocLibSelector)
                return docLibItem;
            else if (menuType == MenuType.DocSelector)
            {
                docLibItem.Text += " Docs";
                foreach (SPListItem item in list.Items)
                {
                    System.Web.UI.WebControls.MenuItem docItem =
                        new System.Web.UI.WebControls.MenuItem(item.File.Name,
                            Uri.EscapeUriString(SPContext.Current.Site.MakeFullUrl(item.File.ServerRelativeUrl)));
                    
                    docLibItem.ChildItems.Add(docItem);
                }
                return docLibItem;
            }
            throw new Exception("Internal error: invalid menu type.");
        }

        private void resetTopMessage()
        {
            Label lblTopMessage = (Label)FindControlRecursive(Page, "lblTopMessage");
            lblTopMessage.Text = string.Empty;
        }

        /// <summary>
        /// Generates the boolean, isPostBack, for the client-side javascript to use.
        /// </summary>
        private void generateIsPostBackJSVar()
        {
            if (IsPostBack)
            {
                ClientScript.RegisterClientScriptBlock(GetType(), "IsPostBack", "var isPostBack = true;", true);
            }
            else
            {
                ClientScript.RegisterClientScriptBlock(GetType(), "IsPostBack", "var isPostBack = false;", true);
            }
        }

        /// <summary>
        /// <para>Updates the repeater to display the parameter form for the selected report definition.</para>
        /// <para>If the selected report definition is invalid, this will do nothing but display an error message.</para>
        /// </summary>
        public void bindRepeater()
        {
            resetTopMessage();
            try
            {
                // reset the parameter index
                parmInd = 0;

                // get the report definition url
                HtmlInputControl txtRptDef = (HtmlInputControl)FindControlRecursive(Page, "txtRptDef");
                string reportDefinitionPath = txtRptDef.Value;
                try
                {
                    ValidateReportDefinitionURL(reportDefinitionPath);
                }
                catch (UriFormatException uex)
                {
                    displayError("The report definition URL is invalid. " + uex.Message);
                    resetRepeater();
                    return;
                }

                // get the report definition
                ReportDefinition rptDef = null;
                try
                {
                    rptDef = new ReportDefinition(reportDefinitionPath);
                }
                catch (Exception ex)
                {
                    displayError("The report definition is invalid. " + ex.Message);
                    resetRepeater();
                    return;
                }

                // create the template parameter collection
                TemplateMapItemCollection parameters = new TemplateMapItemCollection();
                for (int i = 0; rptDef.variableNameArray != null && i < rptDef.variableNameArray.Length; i++)
                {
                    TemplateMapItem newParameter = new TemplateMapItem(0);
                    newParameter.Name = rptDef.variableNameArray[i];
                    newParameter.Description = rptDef.variableDescriptionArray[i];
                    switch (rptDef.variableTypeArray[i].ToUpper())
                    {
                        case "CURRENCY":
                            newParameter.Type = TemplateMapItem.ValueType.CURRENCY;
                            break;
                        case "DATE":
                            newParameter.Type = TemplateMapItem.ValueType.DATE;
                            break;
                        case "INTEGER":
                            newParameter.Type = TemplateMapItem.ValueType.INTEGER;
                            break;
                        case "NUMBER":
                            newParameter.Type = TemplateMapItem.ValueType.NUMBER;
                            break;
                        case "TEXT":
                            newParameter.Type = TemplateMapItem.ValueType.TEXT;
                            break;
                        default:
                            newParameter.Type = TemplateMapItem.ValueType.TEXT;
                            break;
                    }
                    newParameter.Required = Boolean.Parse(rptDef.variableRequiredArray[i]);
                    parameters.Add(newParameter);
                }

                // bind the repeater
                Repeater rptrParms = (Repeater)FindControlRecursive(Page, "rptrParms");
                rptrParms.DataSource = parameters;
                rptrParms.DataBind();
            }
            catch (Exception ex)
            {
                displayError("An error occured while updating the parameters.");
                resetRepeater();
            }
        }

        /// <summary>
        /// Removes controls from the parameter repeater.
        /// </summary>
        private void resetRepeater()
        {
            Repeater rptrParms = (Repeater)FindControlRecursive(Page, "rptrParms");
            rptrParms.Controls.Clear();
        }

        private void displayError(string errorMessage)
        {
            Label lblTopMessage = (Label)FindControlRecursive(Page, "lblTopMessage");
            lblTopMessage.Text = "<p><b><span style=\"color: red\">ERROR:&nbsp;</span>";
            lblTopMessage.Text += errorMessage;
            lblTopMessage.Text += "</b></p>";
        }

        /// <summary>
        /// validate the report definition url,
        /// check if the url is a valid url,
        /// then check if the url points to a valid report definition that we can access,
        /// if any checks fail, throw an invalid argument exception
        /// </summary>
        /// <param name="reportDefinitionPath"></param>
        private static void ValidateReportDefinitionURL(string reportDefinitionPath)
        {
            if (string.IsNullOrEmpty(reportDefinitionPath) || !Uri.IsWellFormedUriString(reportDefinitionPath, UriKind.Absolute))
            {
                throw new UriFormatException("The report definition URL is invalid.");
            }

        }

        /// <summary>
        /// This is the same as Control.FindControl method, but searches recursively.
        /// </summary>
        /// <param name="root">Control to begin search at.</param>
        /// <param name="id">ID of target control.  Will return root if root has the target ID.</param>
        /// <returns></returns>
        private Control FindControlRecursive(Control root, string id)
        {
            if (root.ID == id)
            {
                return root;
            }

            foreach (Control c in root.Controls)
            {
                Control t = FindControlRecursive(c, id);
                if (t != null)
                {
                    return t;
                }
            }

            return null;
        }

        /// <summary>
        /// Fills parameter form controls with default values
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void TemplateControl_DataBinding(object sender, EventArgs e)
        {
            Control theControl = (Control)sender;
            if (theControl.ID == "mapName")
            {
                Label mapName = (Label)theControl;
                RepeaterItem container = (RepeaterItem)mapName.NamingContainer;
                mapName.Text = (string)DataBinder.Eval(container.DataItem, "Name");
            }
            else if (theControl.ID == "mapType")
            {
                HiddenField mapType = (HiddenField)theControl;
                RepeaterItem container = (RepeaterItem)mapType.NamingContainer;
                TemplateMapItem.ValueType type = (TemplateMapItem.ValueType)DataBinder.Eval(container.DataItem, "Type");
                switch (type)
                {
                    case TemplateMapItem.ValueType.CURRENCY:
                        mapType.Value = "CURRENCY";
                        break;
                    case TemplateMapItem.ValueType.DATE:
                        mapType.Value = "DATE";
                        break;
                    case TemplateMapItem.ValueType.INTEGER:
                        mapType.Value = "INTEGER";
                        break;
                    case TemplateMapItem.ValueType.NUMBER:
                        mapType.Value = "NUMBER";
                        break;
                    case TemplateMapItem.ValueType.TEXT:
                        mapType.Value = "TEXT";
                        break;
                    default:
                        mapType.Value = "TEXT";
                        break;
                }
            }
            else if (theControl.ID == "mapDesc")
            {
                Label mapDesc = (Label)theControl;
                RepeaterItem container = (RepeaterItem)mapDesc.NamingContainer;
                mapDesc.Text = (string)DataBinder.Eval(container.DataItem, "Description");
            }
            else if (theControl.ID == "multiView")
            {
                MultiView multiView = (MultiView)theControl;
                RepeaterItem container = (RepeaterItem)multiView.NamingContainer;
                TemplateMapItem.ValueType type = (TemplateMapItem.ValueType)DataBinder.Eval(container.DataItem, "Type");
                switch (type)
                {
                    case TemplateMapItem.ValueType.CURRENCY:
                        multiView.ActiveViewIndex = 0;
                        break;
                    case TemplateMapItem.ValueType.DATE:
                        multiView.ActiveViewIndex = 1;
                        break;
                    case TemplateMapItem.ValueType.INTEGER:
                        multiView.ActiveViewIndex = 0;
                        break;
                    case TemplateMapItem.ValueType.NUMBER:
                        multiView.ActiveViewIndex = 0;
                        break;
                    case TemplateMapItem.ValueType.TEXT:
                        multiView.ActiveViewIndex = 0;
                        break;
                }
            }
        }

        protected void mnuReportDefinition_Click(object sender, MenuEventArgs e)
        {
            // insert the selected url to the text box
            HtmlInputHidden txtRptDef = (HtmlInputHidden)FindControlRecursive(Page, "txtRptDef");
            txtRptDef.Value = e.Item.Value;

            // update the feedback label
            Label lblRptDef = (Label)FindControlRecursive(Page, "lblRptDef");
            lblRptDef.Text = e.Item.Value;

            // clear out all the repeater client ID arrays
            //bugbug: put in bindRepeater?
            string script = "mapNames = []; mapTypes = []; values = []; mapCols = [];";
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "ClearRptrArrays" + parmInd.ToString(), script, true);

            // update the repeater
            bindRepeater();
        }

        protected void mnuSaveInDocLib_Click(object sender, MenuEventArgs e)
        {
            // parse the value
            string[] values = e.Item.Value.Split(' ');
            if (values.Length < 2)
            {
                displayError("Invalid document library selection.");
            }
            string siteUrl = values[0];
            string docLibUrl = values[1];

            // get the site url
            HtmlInputHidden txtSaveInSite = (HtmlInputHidden)FindControlRecursive(Page, "txtSaveInSite");
            txtSaveInSite.Value = siteUrl;

            // get the doc lib url
            HtmlInputHidden txtSaveInDocLib = (HtmlInputHidden)FindControlRecursive(Page, "txtSaveInDocLib");
            txtSaveInDocLib.Value = docLibUrl;

            Label lblSaveInDocLib = (Label)FindControlRecursive(Page, "lblSaveInDocLib");
            lblSaveInDocLib.Text = docLibUrl;
        }

        protected void rptrParms_ItemDataBound(object sender, RepeaterItemEventArgs e)
        {
            Label mapName = (Label)FindControlRecursive(e.Item, "mapName");
            if (mapName != null)
            {
                // get the mapName control's id
                string script = "mapNames[" + parmInd.ToString() + "] = \"" + mapName.ClientID + "\";";
                Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MapNames" + parmInd.ToString(), script, true);

                // get the mapType control's id
                HiddenField mapType = (HiddenField)FindControlRecursive(e.Item, "mapType");
                if (mapType == null)
                {
                    displayError("Internal error: null mapType for parameter " + parmInd.ToString() + ".");
                    return;
                }   
                script = "mapTypes[" + parmInd.ToString() + "] = \"" + mapType.ClientID + "\";";
                Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MapTypes" + parmInd.ToString(), script, true);

                // get the text or calendar control's id
                TextBox varText = (TextBox)FindControlRecursive(e.Item, "varText");
                if (varText != null)
                {
                    // the var is a string-type, write its value to the value array
                    script = "values[" + parmInd.ToString() + "] = \"" + varText.ClientID + "\";";
                    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "Values" + parmInd.ToString(), script, true);
                }
                else
                {
                    Calendar varCal = (Calendar)FindControlRecursive(e.Item, "varCal");
                    if (varCal == null)
                    {
                        displayError("Internal error: null value control for parameter " + parmInd.ToString() + ".");
                        return;
                    }

                    // the var is a calendar type, write its value to the value array
                    //varCal.SelectedDate;
                }

                // get the map to column control's id
                CheckBox mapCol = (CheckBox)FindControlRecursive(e.Item, "mapCol");
                if (mapCol == null)
                {
                    displayError("Internal error: null map-to-column control for parameter " + parmInd.ToString() + ".");
                    return;
                }
                script = "mapCols[" + parmInd.ToString() + "] = \"" + mapCol.ClientID + "\";";
                Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MapCols" + parmInd.ToString(), script, true);

                parmInd++;
            }
        }
    }

    #region TemplateMapItem
    /// <summary>
    /// A single key-value map pair used when running a report. Each pair must be set before the report is run.
    /// </summary>
    internal sealed class TemplateMapItem
    {

        /// <summary>
        /// The type for the value. Warning these numbers match values for the dropdownlist - don't change.
        /// </summary>
        public enum ValueType : int
        {
            /// <summary>
            /// Stored as a float ex: "1.23" but will be parsed as a money amount.
            /// </summary>
            CURRENCY = 0,
            /// <summary>
            /// Stored as a date ex: "01/02/2003" but will be prompted with a calander.
            /// </summary>
            DATE = 1,
            /// <summary>
            /// Must be an integer.
            /// </summary>
            INTEGER = 2,
            /// <summary>
            /// Is a double in the form "1.23"
            /// </summary>
            NUMBER = 3,
            /// <summary>
            /// Is one of the values returned in column 0 from the select.
            /// </summary>
            SELECT = 4,
            /// <summary>
            /// Any text.
            /// </summary>
            TEXT = 5
        };

        private int reportTemplateId;
        private string name;
        private bool required;
        private ValueType type;
        private string description;
        private string select;
        private int datasourceId;

        /// <summary>
        /// Create a TemplateMap object.
        /// </summary>
        /// <param name="reportTemplateId">The PK of the report this is for.</param>
        public TemplateMapItem(int reportTemplateId)
        {
            this.reportTemplateId = reportTemplateId;
            type = ValueType.TEXT;
            //datasourceId = DbUtils.NullPrimaryKey;
        }

        /// <summary>
        /// Creates a copy.
        /// </summary>
        /// <param name="src">The source object.</param>
        public TemplateMapItem(TemplateMapItem src)
        {
            reportTemplateId = src.reportTemplateId;
            name = src.name;
            required = src.required;
            type = src.type;
            description = src.description;
            select = src.select;
            datasourceId = src.datasourceId;
        }

        /// <summary>
        /// The PK of the report this is for.
        /// </summary>
        public int ReportTemplateId
        {
            get { return reportTemplateId; }
        }

        /// <summary>
        /// The key part of the key-value pair.
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// True if this must have a value to run the report.
        /// </summary>
        public bool Required
        {
            get { return required; }
            set { required = value; }
        }

        /// <summary>
        /// What type the value is. All values are stored as a string, this is used for prompting and validation of 
        /// the returned value only.
        /// </summary>
        public ValueType Type
        {
            get { return type; }
            set { type = value; }
        }

        /// <summary>
        /// True if this type uses the select property.
        /// </summary>
        public bool UsesSelect
        {
            get { return type == ValueType.SELECT; }
        }

        /// <summary>
        /// A description of what this map value is used for.
        /// </summary>
        public string Description
        {
            get { return description; }
            set { description = value; }
        }

        /// <summary>
        /// A select used to validate the value. This select must return all legitimate values in column 0.
        /// </summary>
        public string Select
        {
            get { return select; }
            set { select = value; }
        }

        /// <summary>
        /// The datasource to run the select on.
        /// </summary>
        public int DatasourceId
        {
            get { return datasourceId; }
            set { datasourceId = value; }
        }
    }

    /// <summary>
    /// A collection of TemplateMapItem objects.
    /// </summary>
    internal sealed class TemplateMapItemCollection : Collection<TemplateMapItem>
    {

        /// <summary>
        /// Return the element with this name. Return null if don't have it.
        /// </summary>
        /// <param name="name">The name of this map variable.</param>
        public TemplateMapItem FindByName(string name)
        {
            foreach (TemplateMapItem tmi in this)
                if (tmi.Name == name)
                    return tmi;
            return null;
        }
    }
    #endregion TemplateMapItem
}